package com.pan.admin.module.loanProduct.service.impl;

import com.gitee.apanlh.util.base.BigDecimalUtils;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.ValidParam;
import com.pan.admin.common.annotation.DataScope;
import com.pan.admin.common.annotation.DataSource;
import com.pan.admin.common.constant.HttpStatus;
import com.pan.admin.common.core.domain.entity.SysUser;
import com.pan.admin.common.enums.BalanceOperationType;
import com.pan.admin.common.enums.DataSourceType;
import com.pan.admin.common.exception.ServiceException;
import com.pan.admin.common.transaction.TransactionSupport;
import com.pan.admin.common.utils.DateUtils;
import com.pan.admin.common.utils.SecurityUtils;
import com.pan.admin.module.loanProduct.dao.LoanProductDao;
import com.pan.admin.module.loanProduct.entity.LoanProduct;
import com.pan.admin.module.loanProduct.entity.po.LoanProductRechargePO;
import com.pan.admin.module.loanProduct.entity.qo.LoanProductKeywordQO;
import com.pan.admin.module.loanProduct.entity.vo.LoanProductBalanceOperationVO;
import com.pan.admin.module.loanProduct.lock.LoanProductBalanceOperationLock;
import com.pan.admin.module.loanProduct.service.LoanProductRechargeService;
import com.pan.admin.module.loanProduct.service.LoanProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;


/**
 * 产品Service业务层处理
 *
 * @author ts
 * @date 2023-09-12
 */
@Service
public class LoanProductServiceImpl implements LoanProductService {

    @Autowired
    private LoanProductBalanceOperationLock loanProductBalanceOperationLock;
    @Autowired
    private TransactionSupport transactionSupport;
    @Autowired
    private LoanProductDao loanProductDao;
    @Autowired
    private LoanProductRechargeService productRechargeService;

    @Override
    @Transactional(readOnly = true)
    public List<Map<String, Object>> listLoanProduct(String keyword) {
        return this.loanProductDao.listLoanProduct(keyword);
    }

    @Override
    @Transactional(readOnly = true)
    @DataScope(deptAlias = "d", userAlias = "u")
    public List<Map<String, Object>> listLoanProductWithDataScope(LoanProductKeywordQO keyword) {
        return this.loanProductDao.listLoanProductWithDataScope(keyword);
    }

    /**
     * 查询产品列表(有数据权限)
     *
     * @param loanProduct 产品
     * @return 产品
     */
    @Override
    @Transactional(readOnly = true)
    @DataScope(deptAlias = "d", userAlias = "u")
    public List<LoanProduct> listWithDataScope(LoanProduct loanProduct) {
        return loanProductDao.listWithDataScope(loanProduct);
    }

    /**
     * 根据ID获取列表产品
     *
     * @param id 产品主键
     * @return 产品
     */
    @Override
    @Transactional(readOnly = true)
    public LoanProduct getById(Long id) {
        return loanProductDao.getById(id);
    }

    @Override
    @Transactional(readOnly = true)
    public LoanProduct getByLoanPlanId(Long loanPlanId) {
        return loanProductDao.getByLoanPlanId(loanPlanId);
    }

    /**
     * 根据ID获取列表产品(有数据权限)
     *
     * @param loanProduct 产品信息
     * @return 产品
     */
    @Override
    @Transactional(readOnly = true)
    @DataScope(deptAlias = "d", userAlias = "u")
    public LoanProduct getByIdWithDataScope(LoanProduct loanProduct) {
        return loanProductDao.getByIdWithDataScope(loanProduct);
    }

    /**
     * 查询产品列表
     *
     * @param loanProduct 产品
     * @return 产品
     */
    @Override
    public List<LoanProduct> list(LoanProduct loanProduct) {
        return loanProductDao.list(loanProduct);
    }

    @Override
    @DataSource(DataSourceType.SLAVE)
    public List<LoanProduct> listSlave(LoanProduct loanProduct) {
        return loanProductDao.list(loanProduct);
    }

    /**
     * 新增产品
     *
     * @param loanProduct 产品
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertLoanProduct(LoanProduct loanProduct) {
        // 查重并断言true
        this.checkNameAndAssertTrue(loanProduct.getName(), null);

        int row = 0;

        // 接口地址如果是空白字符串则转为null
        if(ValidParam.isEmpty(loanProduct.getApiDataExchange())) {
            loanProduct.setApiDataExchange(null);
        }
        if(ValidParam.isEmpty(loanProduct.getApiDataExchangeTest())) {
            loanProduct.setApiDataExchangeTest(null);
        }
        if(ValidParam.isEmpty(loanProduct.getApiApplyUrl())) {
            loanProduct.setApiApplyUrl(null);
        }
        if(ValidParam.isEmpty(loanProduct.getApiApplyUrlTest())) {
            loanProduct.setApiApplyUrlTest(null);
        }

        //新增机构
        loanProduct.setCreateTime(DateUtils.getNowDate());
        loanProduct.setBalance(new BigDecimal(0));
        loanProduct.setCreateBy(SecurityUtils.getUsername());
        row = loanProductDao.insertLoanProduct(loanProduct);

        if (row < 1) {
            throw new RuntimeException("回滚");
        }
        return row;
    }

    /**
     * 修改产品
     *
     * @param loanProduct 产品
     * @return 结果
     */
    @Override
    public int updateLoanProduct(LoanProduct loanProduct) {
        // 查重并断言true
        this.checkNameAndAssertTrue(loanProduct.getName(), loanProduct.getId());

        // 接口地址如果是空白字符串则转为null
        if(ValidParam.isEmpty(loanProduct.getApiDataExchange())) {
            loanProduct.setApiDataExchange(null);
        }
        if(ValidParam.isEmpty(loanProduct.getApiDataExchangeTest())) {
            loanProduct.setApiDataExchangeTest(null);
        }
        if(ValidParam.isEmpty(loanProduct.getApiApplyUrl())) {
            loanProduct.setApiApplyUrl(null);
        }
        if(ValidParam.isEmpty(loanProduct.getApiApplyUrlTest())) {
            loanProduct.setApiApplyUrlTest(null);
        }

        loanProduct.setUpdateTime(DateUtils.getNowDate());
        loanProduct.setUpdateBy(SecurityUtils.getUsername());

        // 修改产品的url（会更新null值）
        loanProduct.setBalance(null);
        loanProductDao.updateUrlOfLoanProduct(loanProduct);
        return loanProductDao.updateLoanProduct(loanProduct);
    }

    /**
     * 检验用户权限
     *
     * @param id 产品信息主键
     * @return 结果
     */
    @Override
    public void checkDataScope(Long id) {
        if (!SysUser.isAdmin(SecurityUtils.getUserId())) {
            LoanProduct loanProduct = new LoanProduct();
            loanProduct.setId(id);
            LoanProduct loanProductId = loanProductDao.getByIdWithDataScope(loanProduct);
            if (loanProductId == null) {
                throw new ServiceException("没有权限访问用户数据！");
            }
        }
    }

    /**
     * 修改产品的状态
     *
     * @param loanProduct 产品
     * @return 结果
     */
    @Override
    public int updateStatusOfLoanProduct(LoanProduct loanProduct) {
        LoanProduct po = new LoanProduct();
        po.setStatus(loanProduct.getStatus());
        po.setId(loanProduct.getId());
        return loanProductDao.updateLoanProduct(po);
    }

    /**
     * 批量删除产品
     *
     * @param ids 需要删除的产品主键
     * @return 结果
     */
    @Override
    public int batchDeleteById(Long[] ids) {
        return loanProductDao.batchDeleteById(ids);
    }

    @Override
    public void balanceOperation(LoanProductBalanceOperationVO balanceOperationVO) {
        loanProductBalanceOperationLock.executeInLock(balanceOperationVO.getId(), () -> {
            transactionSupport.requiresNew(() -> {
                LoanProduct loanProduct = loanProductDao.getById(balanceOperationVO.getId());

                // 【操作类型】
                Integer type = balanceOperationVO.getBalanceType();
                // 【操作金额】
                BigDecimal money = balanceOperationVO.getMoney();
                // 【余额】
                BigDecimal balance = loanProduct.getBalance();
                // 【剩余余额】
                BigDecimal remainingBalance = BigDecimal.ZERO;

                // 计算【剩余余额】
                if (Objects.equals(type, BalanceOperationType.DEDUCT.getTypeValue())) {
                    remainingBalance = balance.subtract(money);
                } else if (Objects.equals(type, BalanceOperationType.RECHARGE.getTypeValue())) {
                    remainingBalance = balance.add(money);
                } else if (Objects.equals(type, BalanceOperationType.SUPPLEMENT.getTypeValue())) {
                    remainingBalance = balance.add(money);
                } else {
                    return;
                }

                if (Objects.equals(type, BalanceOperationType.DEDUCT.getTypeValue())) {
                    // 余额不足则不允许扣款
                    if (BigDecimalUtils.compare(remainingBalance, 0) < 0) {
                        throw new ServiceException("余额不足，请充值后重新扣款", HttpStatus.ERROR);
                    }
                }

                LoanProductRechargePO recharge = new LoanProductRechargePO();
                recharge.setLoanProductId(loanProduct.getId());
                recharge.setAmount(balanceOperationVO.getMoney());
                recharge.setAccountBalance(remainingBalance);
                recharge.setCurrentBalance(balance);
                recharge.setType(balanceOperationVO.getBalanceType());
                recharge.setCreateBy(SecurityUtils.getUsername());
                recharge.setCreateTime(new Date());
                recharge.setRemark(balanceOperationVO.getRemark());
                productRechargeService.insertLoanProductRecharge(recharge);

                LoanProduct update = new LoanProduct();
                update.setId(loanProduct.getId());
                update.setBalance(remainingBalance);
                loanProductDao.updateBalance(update);
            });
        });
    }

    /**
     * 删除产品信息
     *
     * @param id 产品主键
     * @return 结果
     */
    @Override
    public int deleteById(Long id) {
        return loanProductDao.deleteById(id);
    }

    /**
     * 查重名称是否重复
     *
     * @param name
     * @param excludeId 需要排除的id，用于修改功能
     */
    @Override
    public boolean checkName(String name, @Nullable Long excludeId) {
        // 非空校验
        Assert.isNotEmpty(name, () -> {
            throw new ServiceException("主体名称不能为空");
        });

        List<LoanProduct> list = loanProductDao.checkName(name, excludeId);

        // 如果没查到数据则没重复
        return list.isEmpty();
    }

    /**
     * 查重并断言true
     *
     * @param name
     * @param excludeId 需要排除的id，用于修改功能
     */
    private void checkNameAndAssertTrue(String name, @Nullable Long excludeId) {
        boolean flag = this.checkName(name, excludeId);
        Assert.isTrue(flag, () -> {
            throw new ServiceException("主体名称已存在,请重新修改主体名称");
        });
    }

}
